/////////// FUNCTIONS


///////// LIGHT FUNCTIONS
float diffuselight( float3 normal, float3 lightvec, float extra)
{
	normal = normalize(normal);
	lightvec = normalize(lightvec);
	
	return saturate(dot(normal,lightvec)+extra); //dot product between surface and light returns how lit the pixel is. clamp between 0 and 1 because intensity is multiplied later
}

float blinnspecular(float3 normal, float3 lightvec, float3 eyevec, float glossiness)
{
	normal = normalize(normal);
	lightvec = normalize(lightvec);
	eyevec = normalize(eyevec);
	
	float3 halfvector = normalize(eyevec+lightvec); //add eye and light together for half vector (Blinn)
	
	float specular;
	specular = dot(halfvector, normal); //dot between half and normal (Blinn)
	specular = pow(specular, glossiness); //power specular to glossiness to sharpen highlight
	specular *= saturate(dot(normal,lightvec) * 4); //fix for Specular through surface bug. what this does is just make sure no specular happens on unlit parts. the multiplier works as a bias
	
	return specular;
}

///// SHADOW FUNCS

#define BLOCKER_SEARCH_NUM_SAMPLES 16 
#define PCF_NUM_SAMPLES 16
//#define NEAR_PLANE 1.0
//#define LIGHT_WORLD_SIZE 0.45 
//#define LIGHT_FRUSTUM_WIDTH 3
 
// Assuming that LIGHT_FRUSTUM_WIDTH == LIGHT_FRUSTUM_HEIGHT 
//#define LIGHT_SIZE_UV (LIGHT_WORLD_SIZE / LIGHT_FRUSTUM_WIDTH) 
 
cbuffer POISSON_DISKS 
{ 
  float2 poissonDisk[16] = { 
   float2( -0.94201624, -0.39906216 ), 
   float2( 0.94558609, -0.76890725 ), 
   float2( -0.094184101, -0.92938870 ), 
   float2( 0.34495938, 0.29387760 ), 
   float2( -0.91588581, 0.45771432 ), 
   float2( -0.81544232, -0.87912464 ), 
   float2( -0.38277543, 0.27676845 ), 
   float2( 0.97484398, 0.75648379 ), 
   float2( 0.44323325, -0.97511554 ), 
   float2( 0.53742981, -0.47373420 ), 
   float2( -0.26496911, -0.41893023 ), 
   float2( 0.79197514, 0.19090188 ), 
   float2( -0.24188840, 0.99706507 ), 
   float2( -0.81409955, 0.91437590 ), 
   float2( 0.19984126, 0.78641367 ), 
   float2( 0.14383161, -0.14100790 ) 
}; 
};

float PenumbraSize(float zReceiver, float zBlocker) //Parallel plane estimation 
{ 
    return (zReceiver - zBlocker) / zBlocker; 
} 
 
void FindBlocker(out float avgBlockerDepth,  
                out float numBlockers, 
                float2 uv, float zReceiver ) 
{ 
    //This uses similar triangles to compute what  
    //area of the shadow map we should search 
    float searchWidth = LightSize * (zReceiver - NearPlane) / zReceiver;
    //float searchWidth = 1000.0f;
 
    float blockerSum = 0; 
    numBlockers = 0; 
     
    for( int i = 0; i < BLOCKER_SEARCH_NUM_SAMPLES; ++i ) 
    { 
        // float shadowMapDepth = ShadowMapTex.SampleLevel(ShadowMapSampler, uv + poissonDisk[i] * searchWidth, 0); 
        float shadowMapDepth = tex2D(ShadowMapSampler, (uv + poissonDisk[i] * searchWidth).xyyy);
        if ( shadowMapDepth < zReceiver ) { 
                blockerSum += shadowMapDepth; 
                numBlockers++; 
            } 
     } 
 
    avgBlockerDepth = blockerSum / numBlockers; 
} 
 
float PCF_Filter( float2 uv, float zReceiver, float filterRadiusUV ) 
{ 
    float sum = 0.0f; 
    for ( int i = 0; i < PCF_NUM_SAMPLES; ++i ) 
    { 
        float2 offset = poissonDisk[i] * filterRadiusUV;
         
        //sum += ShadowMapTex.SampleCmpLevelZero(PCF_Sampler, uv + offset, zReceiver);
        float shadMapDepth = 0;
        shadMapDepth = tex2D(ShadowMapSampler, (uv + offset).xy).x;
        
        float shad = zReceiver < shadMapDepth;
        sum += shad;

    } 
    return sum / PCF_NUM_SAMPLES; 
} 
 
float PCSS ( float4 coords  ) 
{ 
    float2 uv = coords.xy; 
    float zReceiver = coords.z; // Assumed to be eye-space z in this code 
  
    // STEP 1: blocker search 
    float avgBlockerDepth = 0; 
    float numBlockers = 0; 
    FindBlocker( avgBlockerDepth, numBlockers, uv, zReceiver ); 
 
    if( numBlockers < 1 )   
    //There are no occluders so early out (this saves filtering) 
  return 1.0f; 
 
    // STEP 2: penumbra size 
    float penumbraRatio = PenumbraSize(zReceiver, avgBlockerDepth);     
    float filterRadiusUV = penumbraRatio * LightSize * NearPlane / coords.z; 
     
    // STEP 3: filtering 
    return PCF_Filter( uv, zReceiver, filterRadiusUV ); 
} 

